﻿/**
 * ParentPage(parentid)
 1.维护多个子页面和它们的服务列表
 2.注册服务，等待被子页面调用，可被子页面调用此服务
 3.注册事件，等待自己调用，将通知到所有子页面
 4.可以调用子页面的方法 childs[childid].method 来调用
 **/
function ParentPage(parentid) {
    var me = this;
    //子页面
    this.childs = {};
    //子页面服务回调
    this.childsInvokerCallback = {};
    //服务，可被子页面调用
    this.serviceMethods = {};
    //事件，被自己调用，但要通知到子页面
    this.eventMethods = {};
    //发起调用时，如有参数为方法的，保存回调
    this.invokerCallback = {};

    ParentPage.prototype.callback2Child = function (data, todomain) {
        var ifrs = document.getElementsByTagName("iframe");
        if (ifrs) {
            todomain = todomain || '*';
            for (var i = 0; i < ifrs.length; i++) {
                if (todomain != '*') {
                    if (ifrs[i].src != todomain)
                        continue;
                }
                ifrs[i].contentWindow.postMessage(data, todomain);
            }
        }
    }

    ParentPage.prototype.makeParamData = function (store, action, type, target, oriargs) {
        //type有service,event,callback
        var args = [];
        for (var i = 0; i < oriargs.length; i++) {
            if (typeof oriargs[i] == 'function') {
                store[action] = store[action] || {};
                args.push(this.makeCallbackStub4Invoker(store, action, oriargs[i], i));
            } else
                args.push(oriargs[i]);
        }
        var data = {
            action: action,
            type: type,
            target: target,
            parentid: parentid,
            from: 'parentpage',
            fromdomain: window.location.href,
            to: 'childpage',
            args: args
        };
        return data;
    }

    // this.callbackMethods = {};

    //被子页面调用的时候用方法来保持回调存根,方法回调以 /function/stub/方法名/出线位序
    ParentPage.prototype.makeCallbackStub4Invoked = function (oridata, stubname) {
        //来源的data,方法名
        function callback() {
            var data = me.makeParamData(this.invokerCallback, oridata.action, 'callback', oridata.childid, arguments);
            //回调时，要补充一个stubname
            data.stubname = stubname;
            me.callback2Child(data, oridata.fromdomain);
        }

        return callback;
    }

    //为调用者提供回调存根
    ParentPage.prototype.makeCallbackStub4Invoker = function (store, action, func, idx) {
        var stubname = '/function/stub/' + action + '/' + idx;
        store[action][stubname] = func;
        return stubname;
    }

    window.addEventListener("message", function (obj) {
        if (obj.data) {
            if ((obj.data.target == parentid || obj.data.target == '*') && obj.data.from == 'childpage' && obj.data.to == 'parentpage'
                && obj.data.action) {
                var action = obj.data.action;
                var args = [];
                if (obj.data.args) {
                    for (var i = 0; i < obj.data.args.length; i++) {
                        if ('string' == typeof obj.data.args[i] && obj.data.args[i].startsWith('/function/stub/')) {
                            //如果子页面调用父页面时，有参数是回调方法的，那么在父页面同样生成类似的回调方法
                            args.push(me.makeCallbackStub4Invoked(obj.data, obj.data.args[i]));
                        } else {
                            args.push(obj.data.args[i]);
                        }
                    }
                }
                if (obj.data.type == 'service') {
                    if ("function" == typeof me.serviceMethods[action]) {
                        // debugger
                        me.serviceMethods[action].apply(me, args);
                        obj.preventDefault();
                    }
                } else if (obj.data.type == 'callback') {
                    //必然是从子页面的回调
                    if (me.childsInvokerCallback[obj.data.childid] && me.childsInvokerCallback[obj.data.childid][action] && "function" == typeof me.childsInvokerCallback[obj.data.childid][action][obj.data.stubname]) {
                        me.childsInvokerCallback[obj.data.childid][action][obj.data.stubname].apply(me, args);
                        obj.preventDefault();
                    }
                }
            }
        }
    });


    ParentPage.prototype.publishService = function (methodName, callback) {
        this.serviceMethods[methodName] = callback;
    };
    ParentPage.prototype.publishEvent = function (methodName, callback) {
        this.eventMethods[methodName] = function () {
            var data = this.makeParamData(this.invokerCallback, methodName, 'event', "*", arguments);
            this.callback2Child(data, '*');
        };
        this[methodName] = this.eventMethods[methodName];
    };

    ParentPage.prototype.makeEventMethod = function (childid, methodName, parentdata) {
        return function (listener) {
            // 子页面事件通知
            this.childs[childid] = this.childs[childid] || {};
            this.childs[childid][methodName] = listener;
        }
    }

    //提供2个方法供子页面初始化调用
    me.publishService('listParentServices', function (childdata, cb) {
        var services = [];
        for (var methodName in me.serviceMethods) {
            if (methodName != 'listParentServices' && methodName != 'listParentEvents' && methodName != 'childLoad')
                services.push(methodName);
        }
        cb({parentid: parentid, fromdomain: window.location.href}, services);
    })
    me.publishService('listParentEvents', function (childdata, cb) {
        var events = [];
        for (var methodName in me.eventMethods)
            events.push(methodName);
        cb({parentid: parentid, fromdomain: window.location.href}, events);
    })
    //子页面注册服务
    me.publishService('ChildPagePublishService', function (childdata, methodName) {
        var childid = childdata.childid;
        if (childid) {
            me.childs[childid] = me.childs[childid] || {};
            me.childsInvokerCallback[childid] = me.childsInvokerCallback[childid] || {};
            me.childs[childid][methodName] = function () {
                var data = me.makeParamData(me.childsInvokerCallback[childid], methodName, 'service', childid, arguments);
                me.callback2Child(data, childdata.fromdomain);
            };
        }
    })
    return me;
}



